package ks.client.game.wordsteal;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;

import ks.client.interfaces.IGameInterface;

import wordsteal.entities.Cell;
import wordsteal.entities.CellColor;
import wordsteal.entities.GameState;
import wordsteal.entities.Player;
import wordsteal.entities.Tile;
import wordsteal.interfaces.IWordstealApp;
import wordsteal.util.BoardLocation;

/**
 * Shared logic stored here between two controllers.
 * <p>
 * First controller, {@link MakeTurnController} is responsible for
 * playing a turn as received from another client. The other one,
 * {@link SubmitWordController} is responsible for playing current
 * player's turn. Both share a lot of code, which is stored here.
 * 
 * @author George Heineman
 */
public class CommonMoveController {
	
	/** Controller has handle to MainFrame */
	IWordstealApp mf;

	/** Hold callback. */
	IGameInterface callback;
	
	/** Common constructor. */
	protected CommonMoveController(IWordstealApp mainFrame) {
		this.mf = mainFrame;
	}


	/**
	 * finishSubmitWord is called once all validation is completed, it updates the boundaries and entities
	 * accordingly based on the tiles the player has placed on the board
	 * @param words Words formed by the player
	 * @param wordCells Cells containing words
	 * @param playedTileCells Cells with tiles played this turn
	 * 
	 * @return true if game is complete, false otherwise.
	 */
	protected boolean finishSubmitWord(String playerId, ArrayList<String> words, ArrayList<ArrayList<Cell>> wordCells, Hashtable<BoardLocation, Cell> playedTileCells) {
		// Find player from the table
		Player currentPlayer = this.mf.getGame().findPlayer(playerId);
		
		// Add new words to that players word list
		currentPlayer.getWords().addAll(words);
		
		// Update tile owners, current player points, and bonus points
		updateOwnerPoints(wordCells, currentPlayer);
		
		// No longer first turn of the game
		this.mf.getGame().setFirstTurn(false);	

		// Check if a Pink square was used
		if (isTilesOnColorCell(playedTileCells, CellColor.Pink)) {

			currentPlayer.modifyPoints(currentPlayer.getBonusPoints());
			this.mf.getGame().getBoard().clearBoard();
			
			for ( int i = 0; i < this.mf.getGame().getPlayers().size(); i++ ) {
				this.mf.getGame().getPlayers().get(i).clearBonusPoints();
			}
			
			this.mf.getGame().setFirstTurn(true);
		}
		
		// Clear consecutive skips
		this.mf.getGame().setConsecSkips(0);
		
		// Check if player has won the game, if they have won the game, show the winning frame
		boolean complete = true;
		if (currentPlayer.getPoints() >= this.mf.getGame().getPointsToWin()) {
			
			// Update the GUI with the new information
			this.mf.updateGUI();
			
			// Update game state
			GameState gs = new GameState(this.mf.getGame(), "Success", 0, words);
			this.mf.getGame().getGameLog().pushGameState(gs);

		} else {
			// Update game state
			GameState gs = new GameState(this.mf.getGame(), "Success", 0, words);
			this.mf.getGame().getGameLog().pushGameState(gs);
			
			this.mf.setStatus("Success!");
			this.mf.updateGUI();
			complete = false;
		}
		
		// in both cases we want to LOCK the gui because we are now
		// distributing clients and our turn is now over.
		this.mf.lockUI(true);
		return complete;
	}	
	
	
	/**
	 * isTilesOnColorCell verifies that a tile has been placed on a cell with a given color
	 * @param placedTileCells A hashtable of board locations mapped to cells with tiles played this turn
	 * @param color Color to be checked for
	 * @return Whether the word is on the color cell
	 */
	boolean isTilesOnColorCell(Hashtable<BoardLocation, Cell> placedTileCells, CellColor color) {
	    Iterator<BoardLocation> itr = placedTileCells.keySet().iterator();	    
	    while (itr.hasNext()) {
	    	BoardLocation bl = itr.next();
	    	if (placedTileCells.get(bl).color == color){
	    		return true; // No error
	    	}
	    }
	    
		return false;
	}
	
	/**
	 * updateOwnerPoints takes all of the cells involved with the new words on the board and assigns the current
	 * player as the owner of the tiles in the cells, it adds points to the current player and subtracts points
	 * from other players if necessary, it also updates player bonus points
	 * @param wordCells Cells containing the tiles that form the words
	 * @param currentPlayerIndex Index of the current player
	 */
	void updateOwnerPoints(ArrayList<ArrayList<Cell>> wordCells, Player currentPlayer) {		
		
		Iterator<ArrayList<Cell>> wordItr = wordCells.iterator();
		
		while(wordItr.hasNext()) {
			
			Iterator<Cell> cellItr = wordItr.next().iterator();
			
			while(cellItr.hasNext()) {
				
				Cell cell = cellItr.next();
				Tile tile = cell.getTile();
				Player owner = tile.getOwner();
				
				if(owner == null) {
					
					tile.setOwner(currentPlayer);
					currentPlayer.modifyPoints(1);
					
					if(cell.color == CellColor.Orange) {
						
						currentPlayer.addBonusPoints(1);
					}
					
				} else if(owner != currentPlayer) {
					
					tile.setOwner(currentPlayer);
					currentPlayer.modifyPoints(1);
					owner.modifyPoints(-1);
					
					if(cell.color == CellColor.Orange) {
						
						currentPlayer.addBonusPoints(1);
						owner.addBonusPoints(-1);
					}
				}
			}
		}
	}
}
